package domain.simplex;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import utils.exception.SimplexException;
import utils.exception.SimplexInfeasibleException;

//import domain.FeatureGraph;

import entities.concept.DomainConcept;
import entities.concept.DomainConceptEQ;
import entities.concept.DomainConceptLT;
import entities.concept.DomainConceptLTEQ;
import entities.role.Role;

/**
 * All equations must be f <= 111 We assume every f.hashCode >=0, thus
 * satisfying the nonnegativity. max any sum of variables if it's unbound or
 * feasible, then the set is sat unsat otherwise.
 * 
 * @author j55wu
 * 
 */
public class SimplexStandardForm {
	private List<Role> varmap = new ArrayList<Role>();
	private int varCount = 0; // x_0 is reserved
	private static int fixedArraySize = 100;// max 100 vars in total
	private double[][] Aij = new double[fixedArraySize][fixedArraySize];
	private double[] Bi = new double[fixedArraySize];
	private double[] Cj = new double[fixedArraySize];
	private double v = 0;
	private List<Integer> nbVars = new ArrayList<Integer>();
	private List<Integer> bVars = new ArrayList<Integer>();

	public boolean translateDomainConcept(HashSet<DomainConcept<?, ?>> dcs) {
		HashSet<DomainConcept<?, ?>> checked = new HashSet<DomainConcept<?, ?>>();
		this.varmap.add(null);
		for (DomainConcept<?, ?> dc : dcs) {
			if (dc.getLHS() instanceof Role) {
				this.putVarName((Role) dc.getLHS());
			}
			if (dc.getValue() instanceof Role) {
				this.putVarName((Role) dc.getValue());
			}
			if (dc.getLHS() instanceof String
					&& dc.getValue() instanceof String) {
				int comp = ((String) dc.getLHS()).compareTo((String) dc
						.getValue());
				if (comp == 0 && dc instanceof DomainConceptLT || comp < 0
						&& dc instanceof DomainConceptEQ || comp > 0) {
					// clash found
					return false;
				}
				continue;
			}
			checked.add(dc);
		}
		for (DomainConcept<?, ?> dc : checked) {
			translateDomainConcept(dc);
		}
		// instantiate and run solver
		for (int i = 1; i < this.nbVars.size(); i++) {
			Cj[i] = -1;
		}
		SimplexSlackForm sf = new SimplexSlackForm(nbVars, bVars, Aij, Bi, Cj,
				v);
		try {
			Simplex.findSolution(sf);
		} catch (SimplexException e) {
			// unbound
			return true;
		} catch (SimplexInfeasibleException e) {
			// infeasible
			return false;
		}
		return true;
	}

	public void translateDomainConcept(DomainConcept<?, ?> dc) {
		// now we have all the nonbasic vars
		if (dc instanceof DomainConceptEQ) {
			// x=y is the same as x<=y and y<=x
			this.caseAnalysis(dc.getLHS(), dc.getValue(), false);
			this.caseAnalysis(dc.getValue(), dc.getLHS(), false);
		} else if (dc instanceof DomainConceptLT) {
			// x<y is the same as x-y <= -1
			this.caseAnalysis(dc.getLHS(), dc.getValue(), true);
		} else if (dc instanceof DomainConceptLTEQ) {
			// direct x<= y
			this.caseAnalysis(dc.getLHS(), dc.getValue(), false);
		} else {
			System.err.println("domain concept unsupported: " + dc);
		}
		return;
	}

	private void putVarName(Role r) {
		if (!varmap.contains(r)) {
			// item 0 is reserved
			varmap.add(r);
			varCount++;
		}
	}

	private Integer getVarName(Role r) {
		if (varmap.contains(r)) {
			return varmap.indexOf(r);
		}
		return -1;
	}

	private void caseAnalysis(Object left, Object right, boolean lessthan) {

		if (left instanceof Role && right instanceof Role) {
			int xb = ++varCount;
			int var1 = this.getVarName((Role) left);
			int var2 = this.getVarName((Role) right);
			Aij[xb][var1] = 1;
			Aij[xb][var2] = -1;
			// set Bi
			if (lessthan) {
				Bi[xb] = -1;
			} else {
				Bi[xb] = 0;
			}
			// add to var set
			this.addVar(bVars, xb);
			this.addVar(nbVars, var1);
			this.addVar(nbVars, var2);
		} else if (left instanceof Role && right instanceof String) {
			int xb = ++varCount;
			int var1 = this.getVarName((Role) left);
			int intval = ((String) right).hashCode();
			Aij[xb][var1] = 1;
			if (lessthan) {
				Bi[xb] = intval - 1;
			} else {
				Bi[xb] = intval;
			}
			// add to var set
			this.addVar(bVars, xb);
			this.addVar(nbVars, var1);
		} else if (left instanceof String && right instanceof Role) {
			// 10<=x, becomes -x<=-10
			int xb = ++varCount;
			int var2 = this.getVarName((Role) right);
			int intval = -1 * ((String) left).hashCode();
			Aij[xb][var2] = -1;
			if (lessthan) {
				Bi[xb] = intval - 1;
			} else {
				Bi[xb] = intval;
			}
			// add to var set
			this.addVar(bVars, xb);
			this.addVar(nbVars, var2);
		} else {
			// both are strings, not supposed to be here
			System.err.println("Not supposed to happen: error in code.");
		}
	}

	private void addVar(List<Integer> vars, int i) {
		if (!vars.contains(new Integer(i))) {
			vars.add(new Integer(i));
		}
	}

}
